package com.rf.richfitwheel.common.advice;

import com.rf.richfitwheel.common.annotation.ResponseResultBody;
import com.rf.richfitwheel.common.enums.ResultStatus;
import com.rf.richfitwheel.common.exception.ResultException;
import com.rf.richfitwheel.common.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.util.WebUtils;

import javax.validation.ConstraintViolationException;
import java.lang.annotation.Annotation;

/**
 * @Title :
 * @Description : 统一返回页面格式包装
 * @Author : GaoHuiMin
 * @Pate : 2021/9/11 16:11
 * @Param :
 * @return :
 **/
@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }

    /**
     * 判断类或者方法是否使用了 @ResponseResultBody
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
     * 当类或者方法使用了 @ResponseResultBody 就会调用这个方法
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Result) {
            return body;
        }
        //判断boolean
        if (body instanceof Boolean) {
            boolean bl = ((Boolean) body).booleanValue();
            if (bl) {
                return Result.success(ResultStatus.SUCCESS, "成功");
            } else {
                return Result.failure(ResultStatus.FAILURE, "失败");
            }
        }
        return Result.success(body);
    }

    /**
     * 提供对标准Spring MVC异常的处理
     *
     * @param ex      the target exception
     * @param request the current request
     */
//    @ExceptionHandler(Exception.class)
//    public final ResponseEntity<Result<?>> exceptionHandler(Exception ex, WebRequest request) {
//        HttpHeaders headers = new HttpHeaders();
//        if (ex instanceof ResultException) {
//            log.error("ExceptionHandler: {}", ex.getMessage());
//            return this.handleResultException((ResultException) ex);
//        }
//        if (ex instanceof BindException) {
//            log.error("BindException: {}", ex.getMessage());
//            return this.handleBindException((BindException) ex, headers, request);
//        }
//        //入参验证不通过时，抛出了ConstraintViolationException异常
//        if (ex instanceof ConstraintViolationException) {
//            log.error("ConstraintViolationException: {}", ex.getMessage());
//            return this.handleConstraintViolationException((ConstraintViolationException) ex, headers, request);
//        }
//        if (ex instanceof MethodArgumentNotValidException) {
//            log.error("MethodArgumentNotValidException: {}", ex.getMessage());
//            return this.handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, headers, request);
//        }
//        // TODO: 这里可以自定义其他的异常拦截
//        return this.handleException(ex, headers, request);
//    }

    /**
     * 对ResultException类返回返回结果的处理
     */
    @ExceptionHandler(ResultException.class)
    protected ResponseEntity<Result<?>> handleResultException(ResultException ex) {
        Result<?> body;
        // 一律返回200
        HttpStatus status = ex.getResultStatus() != null ? ex.getResultStatus().getHttpStatus() : HttpStatus.OK;
        body = ex.getResultStatus() != null ?
                Result.failure(ex.getResultStatus(), ex.getMessage()) :
                ex.getR() != null ? Result.failure(ex.getR()) : null;
        return new ResponseEntity<>(body, null, status);
    }

    /**
     * 对ConstraintViolationException类返回返回结果的处理
     */
    @ExceptionHandler(ConstraintViolationException.class)
    protected ResponseEntity<Result<?>> handleConstraintViolationException(ConstraintViolationException ex, HttpHeaders headers, WebRequest request) {
        String[] split = ex.getMessage().split(",");
        Result<?> body = Result.failure(ResultStatus.BAD_REQUEST, split[0].split("\\:")[1]);
        HttpStatus status = HttpStatus.BAD_REQUEST;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * 对BindException类返回返回结果的处理
     */
    @ExceptionHandler(BindException.class)
    protected ResponseEntity<Result<?>> handleBindException(BindException ex, HttpHeaders headers, WebRequest request) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败：");
        for (FieldError fe : bindingResult.getFieldErrors()) {
            sb.append(fe.getField()).append(":").append(fe.getDefaultMessage()).append(",");
        }
        String msg = sb.toString();
        Result<String> body = Result.failure(ResultStatus.BAD_REQUEST, msg);
        HttpStatus status = HttpStatus.BAD_REQUEST;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * 对MethodArgumentNotValidException类返回返回结果的处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Result<?>> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpHeaders headers, WebRequest request) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败：");
        for (FieldError fe : bindingResult.getFieldErrors()) {
            sb.append(fe.getField()).append(":").append(fe.getDefaultMessage()).append(",");
        }
        String msg = sb.toString();
        Result<String> body = Result.failure(ResultStatus.FAILURE_VALIDATE, msg);
        HttpStatus status = HttpStatus.BAD_REQUEST;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    @ExceptionHandler(AsyncRequestTimeoutException.class)
    @ResponseBody
    public ResponseEntity<Result<?>> asyncRequestTimeoutExceptionHandler(AsyncRequestTimeoutException e, HttpHeaders headers, WebRequest request) {
        StringBuilder sb = new StringBuilder("接口请求超时：");
        String msg = sb.toString();
        Result<String> body = Result.failure(ResultStatus.BAD_REQUEST, msg);
        HttpStatus status = HttpStatus.BAD_REQUEST;
        return this.handleExceptionInternal(e, body, headers, status, request);
    }

    /**
     * 异常类的统一处理
     */
    protected ResponseEntity<Result<?>> handleException(Exception ex, HttpHeaders headers, WebRequest request) {
        log.info("handleException: {}", ex.getMessage());
        ex.printStackTrace();
        Result<?> body = Result.failure();
        HttpStatus status = HttpStatus.OK;
        // HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleExceptionInternal(java.lang.Exception, java.lang.Object, org.springframework.http.HttpHeaders, org.springframework.http.HttpStatus, org.springframework.web.context.request.WebRequest)
     * <p>
     * A single place to customize the response body of all exception types.
     * <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
     * request attribute and creates a {@link ResponseEntity} from the given
     * body, headers, and status.
     */
    protected ResponseEntity<Result<?>> handleExceptionInternal(
            Exception ex, Result<?> body, HttpHeaders headers, HttpStatus status, WebRequest request) {

        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return new ResponseEntity<>(body, headers, status);
    }


}
